// 其实就是在第三行 执行了第六行传进来的函数，然后把第9行的参数'1'传过去
const handleAsyncErrors = (a) => function (c){ // 2. 这里的 a 就是第六行传进来的函数，这里的 c 就是第九行传进来的 '1'
  a(c)
}
// 1. 这里的 b 变量就是第二行箭头返回的函数，同时把括号里的函数赋值给了第二行的变量a
const b = handleAsyncErrors(function (c){
  console.log(c)
})
b('1')
// 这段代码的写法是一种函数式编程的技巧，叫做柯里化（currying）。柯里化是把一个多参数的函数，转化为单参数函数的过程。柯里化的好处是可以把一个函数的通用性降低，生成一个专用函数，或者实现偏函数应用（partial application），也就是固定一部分参数，返回一个新的函数。

// 在这段代码中，handleAsyncErrors 函数接受一个函数 a 作为参数，返回一个新的函数 b ，这个新的函数 b 又接受一个参数 c ，然后调用 a 函数，并把 c 传给 a 。这样，handleAsyncErrors 就实现了柯里化，把一个两个参数的函数（a 和 c ），转化为一个单参数的函数（b）。

// 这样做的目的可能是为了实现偏函数应用，也就是固定 a 函数，生成一个专门处理异步错误的函数 b 。这个 b 函数可以接受任意的 c 参数，然后调用 a 函数处理 c 。这样可以避免重复写 a 函数，提高代码的复用性和可读性。


// 这段代码的优点有：

// 它可以实现偏函数应用，也就是固定一部分参数，返回一个新的函数，提高了代码的复用性和可读性。
// 它可以避免副作用，也就是函数不会修改外部的状态或产生其他影响，这样可以保证函数的可预测性和纯度。
// 它可以利用高阶函数抽象出通用的逻辑，例如 handleAsyncErrors 函数可以处理任何异步错误，而不需要重复写相同的代码。

// 这段代码的缺点有：

// 它可能不够直观，需要理解柯里化和高阶函数的概念和用法，对于初学者来说可能有一定的难度。
// 它可能影响性能，因为每次调用柯里化或高阶函数都会创建一个新的函数对象，这会增加内存的开销和垃圾回收的压力。
// 它可能不适合处理复杂的状态变化，因为函数式编程倾向于使用不可变的数据结构，这会导致频繁地复制和更新数据，而不是直接修改数据。

// 如果你在 b 函数中，对 c 的参数值或者参数类型做判断，来走不同的逻辑，那么你并没有避免我说的第三个缺点，而是违背了柯里化的原则。

// 柯里化的原则是把一个多参数的函数，转化为单参数函数的过程。 如果你在 b 函数中，根据 c 的不同情况，执行不同的逻辑，那么你实际上是把 b 函数变成了一个多参数的函数，因为你需要传入 c 的值和类型两个参数。这样就破坏了柯里化的目的，也就是生成一个专用函数，或者实现偏函数应用。

// 如果你想要处理复杂的状态变化，你可以使用其他的编程范式，例如面向对象编程或者响应式编程。 这些编程范式可以更好地处理数据的修改和更新，以及事件的触发和响应。



// ----------------------


let x = 1;
function fn(x) {
    return function(y) {
        console.log(y + (++x));
    }
}
let f = fn(2); 
f(3); //6
fn(4)(5); //10
f(6); //10
console.log(x);

function test(arr){
  if (arr.length !== 2) return []
  if (arr[0] == arr[1]) return []
  const firstYear = new Date(arr[0]).getFullYear();
  const firstMonth = new Date(arr[0]).getMonth() + 1
  const secondYear = new Date(arr[1]).getFullYear()
  const secondMonth = new Date(arr[1]).getMonth() + 1
  const firstDate = new Date(arr[0])

  if (firstYear > secondYear) return []
  if ((firstYear >= secondYear) && firstMonth > secondMonth) return []
  const datesArr = []
  let i = 0
  while(true){
    datesArr.push(new Date( new Date(arr[0]).setDate(firstDate.getDate() + i) ).toLocaleDateString())
    console.log(new Date( new Date(arr[0]).setDate(firstDate.getDate() + i) ).toLocaleDateString())
    if (new Date( new Date(arr[0]).setDate(firstDate.getDate() + i) ).toLocaleDateString() == arr[1]) break
    i++;
  }
  return datesArr
}

test(['2021-4-30', '2021-5-3'])
// const a = new Date('2021-04-30')
// const b = new Date('2021-04-30').setDate(31)
// console.log(new Date(a).toLocaleDateString() == '2021-04-30' )


// -----------------
// bitmap只能存储整数，不能存储浮点数、对象、字符串、无法还原原数组顺序、无法重复存储整数
// 如果有一个数组有一亿个整数，那么只要找出最大的那个数，除8的结果加一就可以作为Buffer.alloc的参数了，这样一来，就可以把一亿个整数用bitmap存储了
// 创建一个长度为8的Buffer，用0填充；那么这个buffer对象，也就是bitmap变量，每8个0，就是一字节。如果传 419430400, 1 就是会立刻生成 400MB 的buffer，可以观察内存占用
let bitmap = Buffer.alloc(154999, 0);

// 定义一个函数，用于将一个整数添加到bitmap中
function add(num) {
    // 计算该整数在bitmap中的位置，即字节索引和位索引；将整数除8得到字节索引，然后再对8进行取余，就是位的索引，并将这个字节的位，设置为1
    let byteIndex = Math.floor(num / 8);
    let bitIndex = num % 8;
    // 将对应的位设为1，使用按位或运算符
    bitmap[byteIndex] |= (1 << bitIndex);
}

// 定义一个函数，用于检查一个整数是否在bitmap中
function contains(num) {
    // 计算该整数在bitmap中的位置，即字节索引和位索引
    let byteIndex = Math.floor(num / 8);
    let bitIndex = num % 8;
    // 检查对应的位是否为1，使用按位与运算符
    return (bitmap[byteIndex] & (1 << bitIndex)) !== 0;
}

// 测试
add(0);
add(1234567);
add(4);
add(6);

console.log(contains(1234567));



// 创建一个空数组，用来存储bitmap中的整数
let nums = [];

// 用一个循环来遍历Buffer中的每个字节
for (let i = 0; i < bitmap.length; i++) {
    // 用一个循环来遍历每个字节中的每个位
    for (let j = 0; j < 8; j++) {
        // 判断每个位是否为1，使用按位与运算符
        if (bitmap[i] & (1 << j)) {
            // 如果为1，就说明对应的整数存在，计算出这个整数，使用位移和加法运算符
            let num = (i << 3) + j;
            // 将这个整数输出或存储到数组中
            console.log(num);
            nums.push(num);
        }
    }
}
console.log(nums)

// -----------------------
// 获取最大值，递归最底层是0与1比较，1与2比较，2与3比较
function getMax(arr, left, right) { // 定义一个函数，参数是数组和左右边界
  if (left == right) { // 如果左右边界相等，说明只有一个元素
      return arr[left]; // 返回该元素作为最大值
  } else { // 否则
      var mid = Math.floor((left + right) / 2); // 计算中间位置
      var maxLeft = getMax(arr, left, mid); // 求出左半部分的最大值
      var maxRight = getMax(arr, mid + 1, right); // 求出右半部分的最大值
      return Math.max(maxLeft, maxRight); // 返回两者中较大的一个作为整个区间的最大值
  }
}
// 给arr生成一亿个整数
const arr = []
console.log(process.memoryUsage())
for (let index = 0; index < 100000000; index++) {
  arr.push(index);
  
}
console.log(process.memoryUsage())
// 递归找出最大值
console.time('a')
console.log(getMax(arr, 0, arr.length - 1)); // 输出整个数组的最大值
console.timeEnd('a')

// 遍历找出最大值
var max = arr[0]; // 设置第一个元素为初始值
console.time('b')
for (var i = 1; i < arr.length; i++) { // 遍历数组
    if (arr[i] > max) { // 如果当前元素大于 max
        max = arr[i]; // 更新 max 的值
    }
}
console.timeEnd('b')
console.log(max);

// -----------------------
const pt = require('puppeteer');
~async function (){
  const browser = await pt.launch({ headless: 'new' })
  const page = await browser.newPage()
  // 英译中
  await page.goto(`https://fanyi.baidu.com/#en/zh/${encodeURIComponent(s)}`)
  // 等待翻译结果的'dom'
  await page.waitForSelector('.ordinary-output');
  /* 
    dom 元素为：
      <p class='ordinary-output source-output'>Use Cases</p>
      <p class='ordinary-output target-output clearfix'>
        <span>使用案例</span>
      </p>
      如果遇到空行，则是：<p class="ordinary-output"> <br> </p> ，比如‘Abuse Prevention’的上两行都是空行
      那么p标签里面就有两个 br 换行符，innerText 获取到两个 \n
  */
  const element = await page.$$('.ordinary-output');
  let res = '';
  let flag = -1; // -1是为了跳过第一个 source-output 元素，然后自增变为0，第一个中文翻译不需要加换行符
  for (const content of element) {

    res += await page.evaluate((ele, flagParam) => {
      if (ele.className.includes('target-output')) return (flagParam ? '\n' : '') + ele.innerText
      if (ele.className == 'ordinary-output') return ele.innerText
      return ''
    }, content, flag++);

  }
  console.log(res);
  page.close() //是关闭这个标签页，而不是关闭浏览器
  browser.close();
}()


const s = `Use Cases
Depending on your use case, you may want to switch to a different store.


Abuse Prevention
The default MemoryStore is probably fine.

API Rate Limit Enforcement
The default MemoryStore stores the hit counts for clients in memory, and is thus unsuitable for use when running multiple servers or processes.

If you have multiple processes on a single server (via the node:cluster module), the cluster-memory-store will keep them all in sync without needing an external data store.

If you have multiple servers, or want to maintain state across app restarts, use an external data store such as redis, memcached, etc..

Alternate Rate Limiters
This module was designed to only handle the basics and didn't even support external stores initially. These other options all are excellent pieces of software and may be more appropriate for some situations:

rate-limiter-flexible
express-brute
rate-limiter`;


// -----------------
const int1 = 1;
const int2 = 2;
!function (){
  outer: if (int1 == 1) {
    if (int2 == 2) {
      break outer; // 跳出外层的if块
    }
    console.log("1");
  }
  console.log(2)
}();
// 或者
const cars = ["BMW", "Volvo", "Saab", "Ford"];
let text = "";
list: {
  text += cars[0] + "<br>";
	text += cars[1] + "<br>";
  text += cars[2] + "<br>";
	break list;
  text += cars[3] + "<br>";
}
console.log(text)


// ----------------
// 让子进程返回响应值，正常来说是把请求参数发给子进程，然后子进程返回给主线程，主线程返回响应值
// 主进程
const http = require ('http');
const child_process = require ('child_process');
const worker = child_process.fork (__dirname + '/uploadToET.js'); // 创建子进程
http.createServer (function (req, res) {
  worker.send ({ 'event': 'start' }, req.socket); // 把socket对象发送给子进程
}).listen (3000); // 监听3000端口

// 子进程
process.on ('message', function (msg, socket) { // 接收来自主进程的消息和socket对象
  if (msg.event === 'start') {
    socket.write ('HTTP/1.1 200 OK\r\n'); // 写入响应的状态行
    socket.write ('Content-Type: text/plain\r\n'); // 写入响应的头部
    socket.write ('\r\n'); // 写入空行，表示头部结束
    socket.write ('Hello from child process'); // 写入响应的正文
    socket.end (); // 结束响应
  }
});

// -----------
// 获取命令的返回值
const cp = require("child_process");

const child = cp.spawn("ping",["www.jshaman.com"]);
child.on("error",console.error);
child.stdout.pipe(process.stdout);
child.stderr.pipe(process.stderr);


// -----------------
<!DOCTYPE html>
<html>
  <head>
    <title></title>
  </head>
  <body>
    <div id="button" style="width: 100px; height: 100px;color:aqua;">asd</div>
  </body>
  <script src="static/wasm_exec.js"></script>
  
  <script>
    const go = new Go();
    WebAssembly.instantiateStreaming(fetch("static/a.wasm"), go.importObject)
      .then((result) => go.run(result.instance))
  </script>
  <script>
      document.querySelector('#button').addEventListener('click', () => {
        const a = HandleEvent(10); // 传入参数1
        console.log(a);
        console.log(HandleEvent.toString());
      })
  </script>
</html>

// go文件 GOOS=js GOARCH=wasm go build -o a.wasm a.go
package main

import "syscall/js"

func handleCount(this js.Value, args []js.Value) interface{} {
	count := args[0].Int()
	return js.ValueOf(count + 1)
}

func main() {
	done := make(chan string, 0)
	js.Global().Set("HandleEvent", js.FuncOf(handleCount))
	<-done
}

// ----------------
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
  console.log(process.pid)
  // 创建子进程
  for (let i = 0; i < numCPUs; i++) {
    const worker = cluster.fork();
    // 让前4个子进程监听3000端口，后4个监听3001端口
    worker.send(i < 4 ? 3000 : 3001);
  }

  cluster.on('exit', (worker, code, signal) => {
    console.log(`Worker ${worker.process.pid} died`);
  });
} else {
  process.on('message', (port) => {
    http.createServer((req, res) => {
      res.writeHead(200);
      res.end(`Process ${process.pid} is listening on port ${port}`);
    }).listen(port, () => {
      console.log(`Worker ${process.pid} started on port ${port}`);
    });
  });
}
// -----------------

// async.mapLimit效果

import axios from 'axios'

export const handQueue = (
  reqs // 请求总数
) => {
  reqs = reqs || []

  const requestQueue = (concurrency) => {
    concurrency = concurrency || 6 // 最大并发数
    const queue = [] // 请求池
    let current = 0

    const dequeue = () => {
      while (current < concurrency && queue.length) {
        current++;
        const requestPromiseFactory = queue.shift() // 出列
        requestPromiseFactory()
          .then(() => { // 成功的请求逻辑
          })
          .catch(error => { // 失败
            console.log(error)
          })
          .finally(() => {
            current--
            dequeue() // 出列执行完任务后，继续执行队列中的任务
          });
      }

    }

    return (requestPromiseFactory) => {
      queue.push(requestPromiseFactory) // 入队
      dequeue()
    }

  }

  const enqueue = requestQueue(6)

  for (let i = 0; i < reqs.length; i++) {

    enqueue(() => axios.get('/api/test' + i))
  }
}
// -----------------